using System;

using Atomic.Libraries.Mathematics.Functions;
using Atomic.Libraries.Mathematics.Optimization.Ipopt;

namespace Atomic.Samples.Optimization
{
	public static class Optimization1
	{
		public static void Sample1()
		{
			// See IpoptHowTo.txt how to get Ipopt to work. It's quite easy and Ipopt is very powerful.

			// Variables.
			Variable x1 = new Variable();
			Variable x2 = new Variable();
			Variable x3 = new Variable();
			Variable x4 = new Variable();

			// Objective function and non-linear constraints.
			Function f = x1 * x4 * (x1 + x2 + x3) + x3;
			Function g1 = x1 * x2 * x3 * x4;
			Function g2 = Function.Sqr(x1) + Function.Sqr(x2) + Function.Sqr(x3) + Function.Sqr(x4);

			// Prepare the optimizer.
			IpoptOptimizer o = new IpoptOptimizer();
			o.Variables.Add(x1, x2, x3, x4);
			o.ObjectiveFunction = f;
			o.Constraints.Add(g1 >= 25.0);
			o.Constraints.Add(g2 == 40.0);
			o.Constraints.Add(1.0 <= x1, x1 <= 5.0);
			o.Constraints.Add(1.0 <= x2, x2 <= 5.0);
			o.Constraints.Add(1.0 <= x3, x3 <= 5.0);
			o.Constraints.Add(1.0 <= x4, x4 <= 5.0);

			// Verbose mode. Show Ipopt convergence.
			o.PrintLevel = 5;

			// Run optimization. Initial point doesn't have to satisfy the constraints.
			IpoptOptimizerResult or = o.RunIpopt(x1 | 1.0, x2 | 5.0, x3 | 5.0, x4 | 1.0);

			Console.WriteLine(or.ReturnCode);
			Console.WriteLine("x1 = " + or.OptimalPoint[x1]);
			Console.WriteLine("x2 = " + or.OptimalPoint[x2]);
			Console.WriteLine("x3 = " + or.OptimalPoint[x3]);
			Console.WriteLine("x4 = " + or.OptimalPoint[x4]);
			Console.WriteLine("f = " + or.OptimalValue);
			Console.WriteLine("g1 = " + g1.Value(or.OptimalPoint));
			Console.WriteLine("g2 = " + g2.Value(or.OptimalPoint));
		}
	}
}
